using System;

using Microsoft.Xna.Framework;

namespace Elephant.Framework
{
    /// <summary>
    /// Represents the base of a component. 
    /// </summary>
    /// <remarks>
    /// A component defines a specific functionality that can be attached to any entity.
    /// Some examples are controllers (logic, AI), rendering and physics.
    /// </remarks>
    public abstract class EntityComponent : IEntityComponent
    {
        Entity owner;

        int updateOrder;

        bool enabled = true;

        #region IEntityComponent Members
        
        /// <summary>
        /// Called when the component has been successfully attached to an entity.
        /// Override this method to handle any local communication (read: within the same entity) between components, and any further initialization.
        /// </summary>
        /// <param name="addDependencies"></param>
        /// <remarks>
        /// This method should be used for *local communication with other components. 
        /// (*Communication between components that reside in the same entity).
        /// </remarks>
        public virtual void Initialize(bool addDependencies)
        {
            if (addDependencies) {
                object[] attributes = this.GetType().GetCustomAttributes(true);

                foreach (object o in attributes) {
                    // components (well, any class) can be tagged with multiple attributes.. so we have to make the type check
                    if (o is ComponentDependency) {
                        ComponentDependency cd = o as ComponentDependency;

                        foreach (Type t in cd.Dependencies) {
                            // compact framework does not contain Type.GetInterface, but only GetInterfaces
                            Type[] interfaces = t.GetInterfaces();

                            for (int j = 0; j < interfaces.Length; j++) {
                                // interfaces[i].ToString() ==  Elephant.Framework.IEntityComponent
                                // interfaces[i].Name ==        IEntityComponent
                                if (interfaces[j].ToString().Equals("Elephant.Framework.IEntityComponent", StringComparison.InvariantCultureIgnoreCase)) {
                                    //IEntityComponent component = Activator.CreateInstance(t) as IEntityComponent;

                                    //owner.Add(component, false);
                                    ComponentFactory.Create(t, owner, false);

                                    break;
                                }
                            }
                        }
                    }
                }
                //
                // following commented code is for an alternative way of specifying dependencies, see ComponentDependency.cs
                //
                /*
                object[] attributes = this.GetType().GetCustomAttributes(true);

                foreach (object attribute in attributes) {
                    if (attribute is ComponentDependency) {
                        ComponentDependency dependency = attribute as ComponentDependency;

                        Type[] interfaces = dependency.Type.GetInterfaces();

                        foreach (Type t in interfaces) {
                            if (t.ToString().Equals("Elephant.Framework.IEntityComponent", StringComparison.InvariantCultureIgnoreCase)) {
                                ComponentFactory.Create(dependency.Type, owner, false, dependency.Parameters);

                                break;
                            }
                        }*/
                        /*
                        foreach (Dependency dependency in componentDependency.Dependencies) {
                            Type[] interfaces = dependency.Type.GetInterfaces();

                            foreach (Type interfac in interfaces) {
                                if (interfac.ToString().Equals("Elephant.Framework.IEntityComponent", StringComparison.InvariantCultureIgnoreCase)) {
                                    ComponentFactory.Create(dependency.Type, owner, false, dependency.Parameters);

                                    break;
                                }
                            }
                        }*/
                /*

                    }
                }*/
            }
        }

        /// <summary>
        /// Called after Initialize, and before the component is updated for the first time.
        /// Override this method to handle any global communication (read: outside the entity) between components.
        /// </summary>
        /// <remarks>
        /// This methods should be used for *global communication with other components.
        /// (*Communication between components not residing in the same entity).
        /// </remarks>
        public virtual void Start() { }

        /// <summary>
        /// Raised once the owner has changed.
        /// </summary>
        public event EventHandler<EntityEventArgs> OwnerChanged;
        /// <summary>
        /// Raised once the owner has changed. 
        /// Override this method to add code to handle when the owner has changed.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnOwnerChanged(EntityEventArgs e)
        {
            if (OwnerChanged != null) {
                OwnerChanged(this, e);
            }
        }

        /// <summary>
        /// Gets or sets the owner.
        /// </summary>
        /// <remarks>
        /// Current implementation only allows for setting the owner once.
        /// </remarks>
        public Entity Owner
        {
            get
            {
                return owner;
            }
            set
            {
                if (owner == null) {
                    owner = value;

                    OnOwnerChanged(new EntityEventArgs(value));
                }
                /*
                if (owner != value) {
                    // todo: should really make sure to remove the reference from the previous owner, and make sure
                    // to add it to the new one.. also "reboot" it (init, start) to clear any references it might hold 
                    // from previous owner
                    // update: hacky fix so we can always assign an owner once, but never again
                    if (owner != null) {
                        owner.RemoveImmediate(this);
                    }

                    owner = value;

                    OnOwnerChanged(new EntityEventArgs(value));
                }*/
            }
        }

        #endregion

        #region IUpdateable Members

        /// <summary>
        /// Raised when the component has been enabled/disabled.
        /// </summary>
        public event EventHandler EnabledChanged;
        /// <summary>
        /// Raised when the component has been enabled/disabled.
        /// Override this method to add code to handle when the component has been enabled/disabled.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        protected virtual void OnEnabledChanged(object sender, EventArgs args)
        {
            if (EnabledChanged != null) {
                EnabledChanged(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Called when the game has determined that logic needs to be processed.
        /// Override this method to add logic specific code.
        /// </summary>
        /// <param name="gameTime"></param>
        public virtual void Update(GameTime gameTime) { }

        /// <summary>
        /// Gets or sets whether the component is enabled or disabled.
        /// Override this property to handle toggling manually.
        /// </summary>
        public virtual bool Enabled
        {
            get
            {
                return enabled;
            }
            set
            {
                if (enabled != value) {
                    enabled = value;

                    OnEnabledChanged(this, EventArgs.Empty);
                }
            }
        }

        /// <summary>
        /// Gets or sets the rank of which this component will be in the update list.
        /// A rank of 0 will be updated prior to a rank of 1, 1 prior to 2 etc.
        /// </summary>
        public int UpdateOrder
        {
            get
            {
                return updateOrder;
            }
            set
            {
                if (updateOrder != value) {
                    updateOrder = value;

                    OnUpdateOrderChanged(EventArgs.Empty);
                }
            }
        }

        /// <summary>
        /// Raised when the UpdateOrder has changed.
        /// </summary>
        public event EventHandler UpdateOrderChanged;
        /// <summary>
        /// Raised when the UpdateOrder has changed.
        /// Override this method to add code to handle when the UpdateOrder has changed.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnUpdateOrderChanged(EventArgs e)
        {
            if (this.UpdateOrderChanged != null) {
                this.UpdateOrderChanged(this, e);
            }
        }

        #endregion

        #region IDisposable Members

        bool disposed;

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing && !disposed) {
                disposed = true;
            }
        }

        ~EntityComponent()
        {
            this.Dispose(false);
        }

        #endregion
    }
}
